package database

import (
	"fmt"
	"go_redis_service/datastruct/dict"
	"go_redis_service/interface/IDatabase"
	"go_redis_service/interface/IDict"
	"go_redis_service/interface/IResp"
	"go_redis_service/lib/logger"
	"go_redis_service/resp/reply"
	"strings"
)

type ExecFunc func(db *DB, args [][]byte) IResp.Reply
type CmdLine [][]byte

type DB struct {
	index int
	data  IDict.Dict
}

func NewDB() *DB {
	return &DB{
		index: 0,
		data:  dict.NewSyncDict(),
	}
}

func (db *DB) Exec(c IResp.Connection, cmdLine CmdLine) IResp.Reply {
	logger.Info(fmt.Sprintf("IP: %s Action: %s", c.GetAddress(), getAction(cmdLine)))
	cmdName := strings.ToLower(string(cmdLine[0]))
	cmd, ok := cmdTable[cmdName]
	if !ok {
		return reply.NewStadardErrReply("ERR unkown command " + cmdName + " ")
	}
	if !validateArity(cmd.arity, cmdLine) {
		return reply.NewTheArgNumErrReply()
	}
	fun := cmd.executor
	return fun(db, cmdLine[1:])
}

func validateArity(arity int, cmdArgs [][]byte) bool {
	argNum := len(cmdArgs)
	if arity >= 0 {
		return argNum == arity
	}
	return argNum >= -arity
}

func (db *DB) Remove(key string) {
	db.data.Remove(key)
}

// Removes
// @Description: 批量删除
// @receiver db
// @param keys
// @return deleted
func (db *DB) Removes(keys ...string) (deleted int) {
	deleted = 0
	for _, key := range keys {
		_, exists := db.data.Get(key)
		if exists {
			db.Remove(key)
			deleted++
		}
	}
	return deleted
}

func (db *DB) GetEntity(key string) (*IDatabase.DataEntity, bool) {
	raw, ok := db.data.Get(key)
	if !ok {
		return nil, false
	}
	entity, _ := raw.(*IDatabase.DataEntity)
	return entity, true
}

func (db *DB) PutEntity(key string, entity *IDatabase.DataEntity) int {
	return db.data.Put(key, entity)
}
func (db *DB) PutIfAbsent(key string, entity *IDatabase.DataEntity) int {
	return db.data.PutIfAbsent(key, entity)
}

func (db *DB) GetKeyList(num int) []string {
	return db.data.RandomKeys(num)
}

func (db *DB) Flush() {
	db.data.Clear()
}

func getAction(cmdLine CmdLine) []string {
	actionList := make([]string, len(cmdLine))
	for _, action := range cmdLine {
		actionList = append(actionList, string(action))
	}
	return actionList
}
